class Clock:
    def __init__(self):
        self._tick = -1

    def tickle(self):
        self._tick += 1
        self.on_tickle()

    def on_tickle(self):
        pass

    def get_tick(self):
        return self._tick

    def set_tick(self, tick):
        self._tick = tick

    def reset_tick(self):
        self._tick = -1


clock = Clock()


class PCB:
    def __init__(self, name, prio, tick_claim):
        self.name = name
        self.prio = prio
        self.tick_come = clock.get_tick()
        self.tick_claim = tick_claim
        self.tick_pass = 0
        self.tick_sleep = 0
        self.state = 'R'  # Ready | Execute | Wait | Finish

    def select(self):
        assert self.state == 'R'
        self.state = 'E'

    def deselect(self):
        assert self.state == 'E'
        self.state = 'R'

    def block(self):
        assert self.state == 'E'
        self.state = 'W'

    def wake(self):
        assert self.state == 'W'
        self.state = 'R'

    def stop(self):
        assert self.state == 'E'
        self.state = 'F'

    def execute(self):
        self.tick_pass += 1
        self.prio -= 1

    def is_stop(self):
        return self.tick_pass == self.tick_claim


class Scheduler:
    def __init__(self):
        self.rs = []
        self.es = []  # 假设只有一个 CPU ，所以只有一个进程执行
        self.ws = []
        self.fs = []

    def new_progress(self, pcb):
        assert isinstance(pcb, PCB)
        self.rs.append(pcb)
        self.sort_rs()

    def sleep_progress(self, tick):  # 只有当前运行中的进程才能睡眠
        assert isinstance(tick, int)
        self.es[0].tick_sleep = tick

    def hook(self, scheduler, text):
        """ 钩子函数，有助于调试显示调度各个阶段的状态 """
        pass

    def sort_rs(self):
        # 将就绪队列根据 1.优先级降序 2.到来时间升序 排序
        self.rs.sort(key=lambda p: - p.prio * (clock.get_tick() + 2) + p.tick_come)

    def schedule(self):
        # 等待列表进程的睡眠时间减一
        for ws_idx in range(len(self.ws) - 1, -1, -1):
            pcb = self.ws[ws_idx]
            pcb.tick_sleep -= 1
            # 如果等待进程睡眠时间结束，则移动到就绪队列
            if pcb.tick_sleep <= 0:
                print(pcb.state)
                self.ws.pop(ws_idx)
                pcb.wake()
                self.rs.append(pcb)
                self.sort_rs()
        # 执行进程
        if len(self.es) > 0:
            self.es[0].execute()
            pcb = self.es.pop(0)
            # 如果进程结束，则移动到完成队列
            if pcb.is_stop():
                pcb.stop()
                self.fs.append(pcb)
            # 如果进程请求睡眠，则移动到等待队列
            elif pcb.tick_sleep > 0:
                pcb.block()
                self.ws.append(pcb)
            # 如果进程没有结束，则移动到就绪队列
            else:
                pcb.deselect()
                self.rs.append(pcb)
                self.sort_rs()
        # 如果没有进程正在执行，则选择就绪队列中的一个进程执行
        if len(self.es) < 1 and len(self.rs) > 0:
            pcb = self.rs.pop(0)
            pcb.select()
            self.es.append(pcb)
        self.hook(self, '---')


# 字符串总结进程信息，用于调试
def str_pcb(pcb):
    assert isinstance(pcb, PCB)
    return '{}[{}]({}/{})'.format(pcb.name, pcb.prio, pcb.tick_pass, pcb.tick_claim)


# 打印各进程队列状态，用于调试
def print_sch(sch):
    assert isinstance(sch, Scheduler)
    print('  Ready:', [str_pcb(pcb) for pcb in sch.rs])
    print('Execute:', [str_pcb(pcb) for pcb in sch.es])
    print('   Wait:', [str_pcb(pcb) for pcb in sch.ws])
    print(' Finish:', [str_pcb(pcb) for pcb in sch.fs])
    print('-' * 40)


# Unit test
if __name__ == '__main__':
    s = Scheduler()
    clock.on_tickle = s.schedule  # 模拟时钟中断处理函数

    s.new_progress(PCB('P1', 10, 5))
    s.new_progress(PCB('P2', 9, 6))
    s.new_progress(PCB('P3', 11, 8))

    def hook(sch, text):
        print(clock.get_tick(), text)
        print_sch(sch)
    s.hook = hook

    while len(s.rs) > 0 or len(s.es) > 0:
        clock.tickle()
